using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using EnvDTE;
using SolutionFactory.Services;
using SolutionFactory.UI;
using SolutionFactory.VSTemplateSchema;
using ProjectItem=EnvDTE.ProjectItem;

namespace SolutionFactory.Exporter
{
    public class VSTemplateFactory
    {
        public static readonly string WizardAssemblyName =
            Path.GetFileNameWithoutExtension(typeof (Wizard).Assembly.Location); //    "SolutionTemplate";

        private static readonly string WizardClassName = typeof (Wizard).FullName;
        private readonly IFileSystem _fileSystem;

        public VSTemplateFactory(IFileSystem fileSystem)
        {
            _fileSystem = fileSystem;
        }

        public VSTemplate GetProjectTemplate(Project project)
        {
            var template = new VSTemplate();
            template.Type = "Project";
            template.Type = "Project";
            template.Version = "2.0.0";
            template.TemplateData = new VSTemplateTemplateData
                                        {
                                            Name = new NameDescriptionIcon {Value = project.Name},
                                            ProjectType = "CSharp",
                                            Description = new NameDescriptionIcon {Value = ""},
                                            Icon = new NameDescriptionIcon {Value = "__TemplateIcon.ico"},
                                            DefaultName = project.Name,
                                            ProvideDefaultName = true,
                                            ProvideDefaultNameSpecified = true,
                                            SortOrder = "1000",
                                            CreateNewFolder = true,
                                            CreateNewFolderSpecified = true,
                                        };

            template.TemplateContent = new VSTemplateTemplateContent();

            template.TemplateContent.Items = new[]
                                                 {
                                                     new VSTemplateTemplateContentProject
                                                         {
                                                             File =
                                                                 _fileSystem.GetFileNameWithExtension(
                                                                 project.FileName),
                                                             Items = GetItems(project).ToArray(),
                                                             TargetFileName = _fileSystem.GetFileNameWithExtension(
                                                                 project.FileName),
                                                             ReplaceParametersSpecified = true,
                                                             ReplaceParameters = true,
                                                         }
                                                 };

            template.WizardExtension = CreateWizardExtention();

            return template;
        }

        public VSTemplateWizardExtension[] CreateWizardExtention()
        {
            return new[]
                       {
                           new VSTemplateWizardExtension
                               {
                                   Assembly = new[] {WizardAssemblyName},
                                   FullClassName = new[] {WizardClassName}
                               },
                       };
        }

        public IEnumerable<object> GetItems(Project project)
        {
            var items = new ArrayList();
            items.AddRange((ICollection) GetChildItems(project.ProjectItems));
            return items.ToArray();
        }

        public IEnumerable<object> GetProjectItems(ProjectItems projectItems)
        {
            foreach (ProjectItem projectItem in projectItems)
            {
                if (projectItem.IsAFile())
                {
                    yield return projectItem;
                }
            }
        }

        public IEnumerable<object> GetFolderItems(ProjectItems projectItems)
        {
            foreach (ProjectItem projectItem in projectItems)
            {
                if (projectItem.IsAFolder())
                {
                    yield return projectItem;
                }
            }
        }

        private ICollection<object> GetChildItems(ProjectItems parentProjectItems)
        {
            var items = new ArrayList();
            foreach (ProjectItem projectItem in GetProjectItems(parentProjectItems))
            {
               
                if (projectItem.IsLinkedFile())
                {
                    if (parentProjectItems.Parent is Project)
                    {
                        items.Add(CreateLinkedFileItem(projectItem,
                                                       ((Project) parentProjectItems.Parent).FileName));
                    }
                    else
                    {
                        items.Add(CreateLinkedFileItem(projectItem,
                                                       ((ProjectItem) parentProjectItems.Parent).get_FileNames(0)));
                    }
                }
                else
                {
                    items.Add(CreateFileItem(projectItem));
                    if(projectItem.ProjectItems!=null)
                    {
                        items.AddRange((ICollection) GetChildItems(projectItem.ProjectItems));
                    }
                }
            }
            foreach (ProjectItem projectItem in GetFolderItems(parentProjectItems))
            {
                items.Add(CreateFolderItem(projectItem));
            }
            return items.ToArray();
        }

        private object CreateLinkedFileItem(ProjectItem projectItem, string parentPath)
        {
            string filename = projectItem.get_FileNames(0);

            string relativepath = PathUtil.RelativePathTo(Path.GetDirectoryName(parentPath), filename);
            return new VSTemplateSchema.ProjectItem
                       {
                           ReplaceParameters = projectItem.ShouldReplaceParameters(),
                           Value = relativepath,
                           TargetFileName = projectItem.Name,
                           ReplaceParametersSpecified = true
                       };
        }

        private Folder CreateFolderItem(ProjectItem projectItem)
        {
            return new Folder
                       {
                           Name = projectItem.Name,
                           TargetFolderName = projectItem.Name,
                           Items = GetChildItems(projectItem.ProjectItems).ToArray()
                       };
        }

        private VSTemplateSchema.ProjectItem CreateFileItem(ProjectItem projectItem)
        {
            return new VSTemplateSchema.ProjectItem
                       {
                           ReplaceParameters = projectItem.ShouldReplaceParameters(),
                           Value = projectItem.Name,
                           TargetFileName = projectItem.Name,
                           ReplaceParametersSpecified = true
                       };
        }

        public void CreateContentFile(string templateZipFilename, TemplateInput model, string tmp)
        {
            string contentXML = VSContent.Create(_fileSystem.GetFileNameWithExtension(templateZipFilename),
                                                 model.Name, model.Description, model.Category, model.SubCategory);

            string contentFile = tmp.Combine("solution.vscontent");
            _fileSystem.WriteFile(contentFile, contentXML);
        }

        private VSTemplate CreateTemplateForSolution(string templateName, string projectType, string projectSubType,
                                                     string description)
        {
            var template = new VSTemplate {TemplateData = new VSTemplateTemplateData()};
            template.TemplateData.Name = new NameDescriptionIcon {Value = templateName};
            template.TemplateData.DefaultName = templateName.Replace(" ", "");
            template.TemplateData.ProvideDefaultName = true;
            template.TemplateData.ProvideDefaultNameSpecified = true;
            template.TemplateData.Description = new NameDescriptionIcon {Value = description};
            template.TemplateData.ProjectType = projectType;
            template.TemplateData.ProjectSubType = projectSubType;
            template.TemplateData.SortOrder = "1";
            template.TemplateData.CreateNewFolder = true;
            template.TemplateData.CreateNewFolderSpecified = true;
            template.TemplateData.LocationFieldSpecified = true;
            template.TemplateData.EnableLocationBrowseButton = true;
            template.TemplateData.EnableLocationBrowseButtonSpecified = true;
            template.TemplateData.BuildOnLoad = true;
            template.Type = "ProjectGroup";
            template.Version = "2.0.0";
            template.TemplateContent = new VSTemplateTemplateContent();
            return template;
        }

        public ProjectTemplateLink CreateProjectLink(Project project, string dir)
        {
            string filename = dir.Combine(project.Name).Combine(project.Name + ".vstemplate");
            return new ProjectTemplateLink
                       {
                           ProjectName = project.Name,
                           Value = filename
                       };
        }

        public void CreateProjectTemplates(List<ProjectTemplateLink> templateLinks, List<VSTemplate> templates,
                                    string dir, Solution solution,string projectFolderName)
        {
            foreach (Project project in solution.Projects.GetCodeProjects())
            {
                    VSTemplate template = GetProjectTemplate(project);
                    templates.Add(template);

                    string directory = projectFolderName + @"\".Combine(solution.GetDirectoryName());
                    templateLinks.Add(CreateProjectLink(project, directory));

                    WriteTemplateToFile(template, dir, project.Name, solution);
            }
        }

        private void WriteTemplateToFile(VSTemplate template, string dir, string projectName, Solution solution)
        {
            dir = dir.Combine(solution.GetDirectoryName());
            string targetDir = Path.Combine(dir, projectName) + "\\";
            string filename = Path.Combine(targetDir, projectName + ".vstemplate");
            _fileSystem.CreateDirectory(targetDir);
            template.Serialize(filename);
        }

        public void CreateSolutionTemplate(List<ProjectTemplateLink> templateLinks, string dir,
                                           TemplateParams templateParams, string solutionParentDirectory,
                                           string solutionStartupProjectName)
        {
            VSTemplate template = CreateTemplateForSolution(templateParams.TemplateName, templateParams.ProjectType,
                                                            templateParams.ProjectSubType, templateParams.Description);
            template.TemplateContent.Items = CreateSolutionItems(templateLinks);
            template.WizardExtension = CreateWizardExtention();


            template.WizardData = CreateSolutionWizardData(templateParams.StartupFileToExecute, solutionParentDirectory,
                                                           solutionStartupProjectName);
            string target = Path.Combine(dir, "solution.vstemplate");
            template.Serialize(target);
        }

        private VSTemplateTemplateContentProjectCollection[] CreateSolutionItems(List<ProjectTemplateLink> templateLinks)
        {
            return new[]
                       {
                           new VSTemplateTemplateContentProjectCollection
                               {
                                   Items = templateLinks.ToArray()
                               }
                       };
        }

        private VSTemplateWizardData[] CreateSolutionWizardData(string startupFileName, string solutionParentDirectory,
                                                                string solutionStartUpProject)
        {
            return new[]
                       {
                           new VSTemplateWizardData
                               {
                                   Any = new XmlElement[]
                                             {
                                                 new WizardDataDirectory(solutionParentDirectory),
                                                 new WizardDataStartupProject(solutionStartUpProject),
                                                 new WizardDataExecuteOnLoad(startupFileName)
                                             }
                               },
                       };
        }
    }
}